/********************************************************************************
* @file    bsp_flash.c
* @author  jianqiang.xue
* @version V1.0.0
* @date    2021-04-15
* @brief   flash读写操作
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

#include "cx32l003_hal_flash.h"

#include "bsp_flash.h"
#include "sys_api.h"

/* Private Includes ----------------------------------------------------------*/
#ifdef LISUN_SDK
#include "ls_syscfg.h"
#define BSP_FLASH_PAGE_SIZE  LS_FLASH_PAGE_SIZE
#else
#define BSP_FLASH_PAGE_SIZE  (512)
#endif

/* Private Define ------------------------------------------------------------*/

/* Private Variables ---------------------------------------------------------*/
/* Public Function Prototypes ------------------------------------------------*/
/**
  * @brief  Flash erase page
  * @param  page_addr: flash addr example,0xFE00(63.5K)
  * @param  page_num: Number of pages to erase
  * @retval status
  */
uint8_t bsp_flash_erase_page(uint32_t page_addr, uint32_t page_num) {
    FLASH_EraseInitTypeDef flash_erase_init_t;
    uint32_t error_page; //记录错误的写入位置

    // 一页 512 字节
    if (page_addr % BSP_FLASH_PAGE_SIZE != 0)
        page_addr -= (page_addr % BSP_FLASH_PAGE_SIZE);
    if (page_addr < 1)
        page_addr = 1;

    flash_erase_init_t.TypeErase   = FLASH_TYPEERASE_PAGES;
    flash_erase_init_t.PageAddress = page_addr;
    flash_erase_init_t.NbPages     = page_num;

    return HAL_FLASH_Erase(&flash_erase_init_t, &error_page);
}

/**
 * @brief  flash 读字节
 * @param  addr: flash地址
 * @retval 字节内容
 */
uint8_t bsp_flash_read_byte(uint32_t addr) {
    return (*((uint8_t*)addr));
}

/**
 * @brief  flash 读多字节
 * @param  addr: flash地址
 * @param  *data: 缓存buff地址
 * @param  len: 读取长度
 * @retval 0--失败 1--成功
 */
bool bsp_flash_read_nbyte(uint32_t addr, uint8_t *data, uint32_t len) {
    if (len == 0) return false;

    for (uint32_t i = 0; i < len; i++)
        *(data + i) = bsp_flash_read_byte(addr + i);
    return true;
}

/**
 * @brief  flash 读半字
 * @param  addr: flash地址
 * @retval 半字内容
 */
uint16_t bsp_flash_read_halfword(uint32_t addr) {
    return (*((uint16_t *)addr));;
}

/**
 * @brief  flash 读字
 * @param  addr: flash地址
 * @retval 字内容
 */
uint32_t bsp_flash_read_word(uint32_t addr) {
    return (uint32_t)((*((uint32_t *)addr)));
}

/**
 * @brief  flash 写字节
 * @param  addr: flash地址
 * @param  data: 字节
 * @retval 0--失败 1--成功
 */
bool bsp_flash_write_byte(uint32_t addr, uint8_t data) {
    if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, addr, data))
        return false;
    return true;
}

/**
 * @brief  flash 写字
 * @param  addr: flash地址
 * @param  data: 字
 * @retval 0--失败 1--成功
 */
bool bsp_flash_write_word(uint32_t addr, uint32_t data) {
    if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data))
        return false;
    return true;
}

/**
 * @brief  flash 写多字节
 * @param  addr: flash地址
 * @param  *data: 数据缓冲区
 * @param  len: 数据长度
 * @retval 0--失败 1--成功
 */
bool bsp_flash_write_nbyte(uint32_t addr, uint8_t *data, uint32_t len) {
    if (len == 0)
        return false;

    for (uint32_t i = 0; i < len; i++)
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (addr + i), *(data + i));
    return true;
}

/**
 * @brief  flash 写多字节(带关闭中断函数)
 * @param  addr: flash地址
 * @param  *data: 数据缓冲区
 * @param  len: 数据长度
 * @retval 0--失败 1--成功
 */
bool bsp_flash_write_nbyte_s(uint32_t addr, uint8_t *data, uint32_t len) {
    sys_disable_irq();
    bool ret = bsp_flash_write_nbyte(addr, data, len);
    sys_enable_irq();
    return ret;
}

bool bsp_flash_is_busy(void) {
    if ((FLASH->CR & 0x04) == 1)
        return true;
    else
        return false;
}

/**
 * @brief  flash搬运 (addr2 搬运到 addr1 ，并擦除addr2)
 * @param  t_addr: 目标地址（addr1）
 * @param  s_addr: 欲搬地址（addr2）
 * @param  size: 字节大小
 * @retval 0--失败 1--成功
 */
bool bsp_flash_carry(uint32_t t_addr, uint32_t s_addr, uint32_t size)
{
    bool ret = true;
    uint32_t word_len;
    uint32_t page_len;
    uint32_t i;

    word_len = (size / sizeof(uint32_t));
    page_len = (size % BSP_FLASH_PAGE_SIZE) == 0 ?
                (size / BSP_FLASH_PAGE_SIZE) : (size / BSP_FLASH_PAGE_SIZE) + 1;

    for (i = 0; i < page_len; i++)
        bsp_flash_erase_page(t_addr + i * BSP_FLASH_PAGE_SIZE, 1);

    while(bsp_flash_is_busy());
    for (i = 0; i < word_len; i++) {
        ret = bsp_flash_write_word(t_addr + i * sizeof(uint32_t),
                                   bsp_flash_read_word(s_addr + i * sizeof(uint32_t)));
        if (ret == false) break;
    }

    if (ret == true) {
        for (i = 0; i < page_len; i++)
            bsp_flash_erase_page(s_addr + i * BSP_FLASH_PAGE_SIZE, 1);
    }
    while(bsp_flash_is_busy());
    return ret;
}
